//---------------------------------------------------------------------------
// CBufferedLineStream Class
// Author : Woo-Hyun Kim
// Email : woorung@nhncorp.com
// Date : Dec. 5, 2007
//---------------------------------------------------------------------------
#include "CBufferedLineStream.h"
//---------------------------------------------------------------------------
CBufferedLineStream::CBufferedLineStream(const char *filename) : CBufferedStream(filename)
{
	eol = END_OF_LINE;
	block = 0;
	blocks = 1;
	block_begin_pos = 0;
	block_end_pos = fsize_;
}
//---------------------------------------------------------------------------
CBufferedLineStream::CBufferedLineStream(const CBufferedLineStream &rhs) : CBufferedStream(*this)
{
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
	pos_ = rhs.pos_;
	fd_ = rhs.fd_;
	fsize_ = rhs.fsize_;

	eol = rhs.eol;
	block = rhs.block;
	block_begin_pos = rhs.block_begin_pos;
	block_end_pos = rhs.block_end_pos;
}
//---------------------------------------------------------------------------
CBufferedLineStream &CBufferedLineStream::operator=(const CBufferedLineStream &rhs)
{
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
cout << "######################################//////////////////////////////////////" << endl;
	pos_ = rhs.pos_;
	fd_ = rhs.fd_;
	fsize_ = rhs.fsize_;

	eol = rhs.eol;
	block = rhs.block;
	block_begin_pos = rhs.block_begin_pos;
	block_end_pos = rhs.block_end_pos;
}
//---------------------------------------------------------------------------
CBufferedLineStream::~CBufferedLineStream()
{
}
//---------------------------------------------------------------------------
int CBufferedLineStream::underflow()
{
	if(gptr()<egptr()) {
		return *gptr();
	}

	if(pos_<block_end_pos) {
		int size = read(fd_, buffer_, rbuf_size_);

		if(pos_+size>block_end_pos) {
			// in order to read & exit until the last eol
			size = block_end_pos - pos_;
		}

		if(size) {
			pos_ += size;

			setg(buffer_, buffer_, buffer_+size);

			return *gptr();
		}
		
		pos_ = fsize_;
		return EOF;
	}

	return EOF;
}
//---------------------------------------------------------------------------
long CBufferedLineStream::assignblock(long block, long blocks)
{
	if(block<0||block>blocks) {
		cerr << "ERROR : bad block assigned from (" << block << ", " << blocks << ")" << endl;
		return -1;
	}

	long a_block_size = fsize_ / blocks;

	this->block = block;
	this->blocks = blocks;

	// make sure that the position of the specific block is the exact position of the total block sizes
	// so, block_begin_pf will be exactly the start position of the block which a user want to mark

	block_begin_pos = block * a_block_size;
	block_end_pos = (block+1) * a_block_size;


/*
	if(block_begin_pos>0) {
		block_begin_pos = adjusted_line_seekg(block_begin_pos);

		long first = block_begin_pos;
		// if the predecessor position following the beign of block ended up with the '\n'
		// for loop stops the very position => that is, first-=2
		first-=2;
		seekg((uint64_t) first);
		for(char c;first<block_end_pos;first++) {
			int size = read(fd_, &c, 1);
			if(size<=0||c==eol) break;
		}
		block_begin_pos = first + 1;
	}

	seekg((uint64_t) block_begin_pos);
*/


	// adjust begin_pos & end_pos by line
#if DEBUG
	cerr << "####[before] " << block_begin_pos << ", " << block_end_pos << endl;
#endif

	adjusted_line_seekg(block_begin_pos, block_end_pos);

#if DEBUG
	cerr << "####[after] " << block_begin_pos << ", " << block_end_pos << endl;
#endif

	return this->tellg();
}
//---------------------------------------------------------------------------
void CBufferedLineStream::adjusted_line_seekg(long &bpos, long &epos)
{
	uint64_t adjust_begin = bpos;
	uint64_t adjust_end = epos;
	uint64_t last = size();

	// if begin is 0, just skip
	if((long)(adjust_begin-2)>=0) {
		// if the prededing position is the end of line,
		// the below for loop must stop at the very position
		adjust_begin -= 2;

		// go to the current begin position
		lseek(fd_,adjust_begin,SEEK_SET);

		// adjust to the begin position by the line
		char c;
		int size;
		for(;;adjust_begin++) {
			size = read(fd_, &c, 1);
			if(size<=0||c==eol) break;
		}

		// found adjusted begin position
		// ---------------------------
		adjust_begin += 1;
		// ---------------------------
	}

	/// if end is eol, just skip
	if(adjust_end<last) {
		// go to the current end position
		lseek(fd_,adjust_end,SEEK_SET);

		// adjust to the end position by the line
		char c;
		int size;
		for(;;adjust_end++) {
			size = read(fd_, &c, 1);
			if(size<=0||c==eol) break;
		}

		// found adjusted end position
		// ---------------------------
		adjust_end += 1;
		// ---------------------------
	}


	// final setting
	// --------------------
	bpos = adjust_begin;
	epos = adjust_end;
	// --------------------


	// reset to the begin position
	lseek(fd_,adjust_begin,SEEK_SET);
	pos_ = adjust_begin;
	setg(buffer_, buffer_, buffer_);
}
//---------------------------------------------------------------------------
uint64_t CBufferedLineStream::seekg(uint64_t pos)
{
	lseek(fd_,pos,SEEK_SET);

	pos_ = pos;

	setg(buffer_, buffer_, buffer_);
}
//---------------------------------------------------------------------------
uint64_t CBufferedLineStream::tellg() 
{
	return pos_ + (gptr() - egptr());
}
//---------------------------------------------------------------------------
uint64_t CBufferedLineStream::size() const 
{
	return fsize_;
}
//---------------------------------------------------------------------------
